
package body Element is

   function File_End_Index (ET : in Element_Type) return Positive_Count is
   begin
      return ET.File_Index + Positive_Count(ET.Size);
   end File_End_Index;

   -- Read Number of bytes from Stream
   procedure Read_Bytes(Target : in out Unsigned_64; Stream : not null access Ada.Streams.Root_Stream_Type'Class; Number : in Positive) is
      Byte : Unsigned_8 := 0;
      begin
      for Tmp in 1..Number loop
         Target := Target * 2**8; -- shift left
         Unsigned_8'Read(Stream, Byte);
         Target := Target + Unsigned_64(Byte);
      end loop;
   end Read_Bytes;
   pragma Inline(Read_Bytes);

   procedure Read_Element_Size_Type(Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Element_Size_Type) is
      Byte : Unsigned_8 := 0;
   begin
      Unsigned_8'Read(Stream, Byte);
      case Byte is
         when 2#10000000# .. 2#11111111# =>
            Item := Element_Size_Type(Byte and 2#01111111#);
         when 2#01000000# .. 2#01111111# =>
            Item := Element_Size_Type(Byte and 2#00111111#);
            Read_Bytes(Unsigned_64(Item), Stream, 1);
         when 2#00100000# .. 2#00111111# =>
            Item := Element_Size_Type(Byte and 2#00011111#);
            Read_Bytes(Unsigned_64(Item), Stream, 2);
         when 2#00010000# .. 2#00011111# =>
            Item := Element_Size_Type(Byte and 2#00001111#);
            Read_Bytes(Unsigned_64(Item), Stream, 3);
         when 2#00001000# .. 2#00001111# =>
            Item := Element_Size_Type(Byte and 2#00000111#);
            Read_Bytes(Unsigned_64(Item), Stream, 4);
         when 2#00000100# .. 2#00000111# =>
            Item := Element_Size_Type(Byte and 2#00000011#);
            Read_Bytes(Unsigned_64(Item), Stream,  5);
         when 2#00000010# .. 2#00000011# =>
            Item := Element_Size_Type(Byte and 2#00000001#);
            Read_Bytes(Unsigned_64(Item), Stream, 6);
         when 2#00000001# =>
            Item := 0;
            Read_Bytes(Unsigned_64(Item), Stream, 7);
         when others =>
            raise Element_Size_Exception;
      end case;
   end Read_Element_Size_Type;

   procedure Read_Element_ID_Type(Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Element_ID_Type) is
      Byte : Unsigned_8 := 0;
   begin
      Unsigned_8'Read(Stream, Byte);
      case Byte is
         when 2#10000000# .. 2#11111111# =>
            Item := Element_ID_Type(Byte);
         when 2#01000000# .. 2#01111111# =>
            Item := Element_ID_Type(Byte);
            Read_Bytes(Unsigned_64(Item), Stream, 1);
         when 2#00100000# .. 2#00111111# =>
            Item := Element_ID_Type(Byte);
            Read_Bytes(Unsigned_64(Item),Stream,  2);
         when 2#00010000# .. 2#00011111# =>
            Item := Element_ID_Type(Byte);
            Read_Bytes(Unsigned_64(Item),Stream,  3);
         when others =>
            raise Element_ID_Exception;
      end case;
   end Read_Element_ID_Type;

   procedure Read_Element_Type(Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Element_Type) is
   begin
      Element_ID_Type'Read(Stream, Item.ID);
      Element_Size_Type'Read(Stream, Item.Size);
   end Read_Element_Type;

   function Value(ET : Element_Type; File : IO.File_Type_Access) return Element_Size_Type is
      Result : Element_Size_Type := 0;
   begin
      Read_Bytes(Unsigned_64(Result), File.File_Access, Positive(ET.Size));
      return Result;
   end Value;

   function ID_Hashed (id: Element_ID_Type) return Ada.Containers.Hash_Type is
   begin
      return Ada.Containers.Hash_Type(id);
   end ID_Hashed;
begin

   declare
      MKV_Elements : constant array (Natural range <>)
      of EBML_Element_Type := (
                               -- EBML
                               (16#1a45dfa3#, To_Unbounded_String("EBML"), CONTAINER),
                               (16#4286#, To_Unbounded_String("EBMLVersion"), UNSIGNED_INTEGER),
                               (16#42f7#, To_Unbounded_String("EBMLReadVersion"), UNSIGNED_INTEGER),
                               (16#42f2#, To_Unbounded_String("EBMLMaxIDLength"), UNSIGNED_INTEGER),
                               (16#42f3#, To_Unbounded_String("EBMLMaxSizeLength"), UNSIGNED_INTEGER),
                               (16#4282#, To_Unbounded_String("DocType"), ASCII_STRING),
                               (16#4287#, To_Unbounded_String("DocTypeVersion"), UNSIGNED_INTEGER),
                               (16#4285#, To_Unbounded_String("DocTypeReadVersion"), UNSIGNED_INTEGER),

                               (16#bf#, To_Unbounded_String("CRC-32"), BINARY),
                               (16#ec#, To_Unbounded_String("Void"), BINARY),

                               (16#1b538667#, To_Unbounded_String("SignatureSlot"), CONTAINER),
                               (16#7e8a#, To_Unbounded_String("SignatureAlgo"), UNSIGNED_INTEGER),
                               (16#7e9a#, To_Unbounded_String("SignatureHash"), UNSIGNED_INTEGER),
                               (16#7ea5#, To_Unbounded_String("SignaturePublicKey"), BINARY),
                               (16#7eb5#, To_Unbounded_String("Signature"), BINARY),
                               (16#7e5b#, To_Unbounded_String("SignatureElements"), CONTAINER),
                               (16#7e7b#, To_Unbounded_String("SignatureElementList"), CONTAINER),
                               (16#6532#, To_Unbounded_String("SignedElement"), BINARY),

                               -- Matroska
                               (16#18538067#, To_Unbounded_String("Segment"), CONTAINER),

                               (16#114d9b74#, To_Unbounded_String("SeekHead"), CONTAINER),
                               (16#4dbb#, To_Unbounded_String("Seek"), CONTAINER),
                               (16#53ab#, To_Unbounded_String("SeekID"), BINARY),
                               (16#53ac#, To_Unbounded_String("SeekPosition"), UNSIGNED_INTEGER),

                               (16#1549a966#, To_Unbounded_String("Info"), CONTAINER),
                               (16#73a4#, To_Unbounded_String("SegmentUID"), BINARY),
                               (16#7384#, To_Unbounded_String("SegmentFilename"), UTF_8_STRING),
                               (16#3cb923#, To_Unbounded_String("PrevUID"), BINARY),
                               (16#3c83ab#, To_Unbounded_String("PrevFilename"), UTF_8_STRING),
                               (16#3eb923#, To_Unbounded_String("NextUID"), BINARY),
                               (16#3e83bb#, To_Unbounded_String("NextFilename"), UTF_8_STRING),
                               (16#4444#, To_Unbounded_String("SegmentFamily"), BINARY),
                               (16#6924#, To_Unbounded_String("ChapterTranslate"), CONTAINER),
                               (16#69fc#, To_Unbounded_String("ChapterTranslateEditionUID"), UNSIGNED_INTEGER),
                               (16#69bf#, To_Unbounded_String("ChapterTranslateCodec"), UNSIGNED_INTEGER),
                               (16#69a5#, To_Unbounded_String("ChapterTranslateID"), BINARY),
                               (16#2ad7b1#, To_Unbounded_String("TimecodeScale"), UNSIGNED_INTEGER),
                               (16#4489#, To_Unbounded_String("Duration"), FLOAT),
                               (16#4461#, To_Unbounded_String("DateUTC"), DATE),
                               (16#7ba9#, To_Unbounded_String("Title"), UTF_8_STRING),
                               (16#4d80#, To_Unbounded_String("MuxingApp"), UTF_8_STRING),
                               (16#5741#, To_Unbounded_String("WritingApp"), UTF_8_STRING),

                               (16#1f43b675#, To_Unbounded_String("Cluster"), CONTAINER),
                               (16#e7#, To_Unbounded_String("Timecode"), UNSIGNED_INTEGER),
                               (16#5854#, To_Unbounded_String("SilentTracks"), CONTAINER),
                               (16#58d7#, To_Unbounded_String("SilentTrackNumber"), UNSIGNED_INTEGER),
                               (16#a7#, To_Unbounded_String("Position"), UNSIGNED_INTEGER),
                               (16#ab#, To_Unbounded_String("PrevSize"), UNSIGNED_INTEGER),
                               (16#a0#, To_Unbounded_String("BlockGroup"), CONTAINER),
                               (16#a1#, To_Unbounded_String("Block"), BINARY),
                               (16#a2#, To_Unbounded_String("BlockVirtual"), BINARY),
                               (16#75a1#, To_Unbounded_String("BlockAdditions"), CONTAINER),
                               (16#a6#, To_Unbounded_String("BlockMore"), CONTAINER),
                               (16#ee#, To_Unbounded_String("BlockAddID"), UNSIGNED_INTEGER),
                               (16#a5#, To_Unbounded_String("BlockAdditional"), BINARY),
                               (16#9b#, To_Unbounded_String("BlockDuration"), UNSIGNED_INTEGER),
                               (16#fa#, To_Unbounded_String("ReferencePriority"), UNSIGNED_INTEGER),
                               (16#fb#, To_Unbounded_String("ReferenceBlock"), SIGNED_INTEGER),
                               (16#fd#, To_Unbounded_String("ReferenceVirtual"), SIGNED_INTEGER),
                               (16#a4#, To_Unbounded_String("CodecState"), BINARY),
                               (16#8e#, To_Unbounded_String("Slices"), CONTAINER),
                               (16#e8#, To_Unbounded_String("TimeSlice"), CONTAINER),
                               (16#cc#, To_Unbounded_String("LaceNumber"), UNSIGNED_INTEGER),
                               (16#cd#, To_Unbounded_String("FrameNumber"), UNSIGNED_INTEGER),
                               (16#cb#, To_Unbounded_String("BlockAdditionID"), UNSIGNED_INTEGER),
                               (16#ce#, To_Unbounded_String("Delay"), UNSIGNED_INTEGER),
                               (16#cf#, To_Unbounded_String("Duration"), UNSIGNED_INTEGER),
                               (16#a3#, To_Unbounded_String("SimpleBlock"), BINARY),
                               (16#af#, To_Unbounded_String("EncryptedBlock"), BINARY),

                               (16#1654ae6b#, To_Unbounded_String("Tracks"), CONTAINER),
                               (16#ae#, To_Unbounded_String("TrackEntry"), CONTAINER),
                               (16#d7#, To_Unbounded_String("TrackNumber"), UNSIGNED_INTEGER),
                               (16#73c5#, To_Unbounded_String("TrackUID"), UNSIGNED_INTEGER),
                               (16#83#, To_Unbounded_String("TrackType"), UNSIGNED_INTEGER),
                               (16#b9#, To_Unbounded_String("FlagEnabled"), UNSIGNED_INTEGER),
                               (16#88#, To_Unbounded_String("FlagDefault"), UNSIGNED_INTEGER),
                               (16#55aa#, To_Unbounded_String("FlagForced"), UNSIGNED_INTEGER),
                               (16#9c#, To_Unbounded_String("FlagLacing "), UNSIGNED_INTEGER),
                               (16#6de7#, To_Unbounded_String("MinCache"), UNSIGNED_INTEGER),
                               (16#6df8#, To_Unbounded_String("MaxCache"), UNSIGNED_INTEGER),
                               (16#23e383#, To_Unbounded_String("DefaultDuration"), UNSIGNED_INTEGER),
                               (16#23314f#, To_Unbounded_String("TrackTimecodeScale"), FLOAT),
                               (16#537f#, To_Unbounded_String("TrackOffset"), SIGNED_INTEGER),
                               (16#55ee#, To_Unbounded_String("MaxBlockAdditionID"), UNSIGNED_INTEGER),
                               (16#536e#, To_Unbounded_String("Name"), UTF_8_STRING),
                               (16#22b59c#, To_Unbounded_String("Language"), ASCII_STRING),
                               (16#86#, To_Unbounded_String("CodecID"), ASCII_STRING),
                               (16#63a2#, To_Unbounded_String("CodecPrivate"), BINARY),
                               (16#258688#, To_Unbounded_String("CodecName"), UTF_8_STRING),
                               (16#7446#, To_Unbounded_String("AttachmentLink"), UNSIGNED_INTEGER),
                               (16#3a9697#, To_Unbounded_String("CodecSettings"), UTF_8_STRING),
                               (16#3b4040#, To_Unbounded_String("CodecInfoURL"), ASCII_STRING),
                               (16#26b240#, To_Unbounded_String("CodecDownloadURL"), ASCII_STRING),
                               (16#aa#, To_Unbounded_String("CodecDecodeAll"), UNSIGNED_INTEGER),
                               (16#6fab#, To_Unbounded_String("TrackOverlay"), UNSIGNED_INTEGER),
                               (16#6624#, To_Unbounded_String("TrackTranslate"), CONTAINER),
                               (16#66fc#, To_Unbounded_String("TrackTranslateEditionUID"), UNSIGNED_INTEGER),
                               (16#66bf#, To_Unbounded_String("TrackTranslateCodec"), UNSIGNED_INTEGER),
                               (16#66a5#, To_Unbounded_String("TrackTranslateTrackID"), BINARY),

                               (16#e0#, To_Unbounded_String("Video"), CONTAINER),
                               (16#9a#, To_Unbounded_String("FlagInterlaced"), UNSIGNED_INTEGER),
                               (16#53b8#, To_Unbounded_String("StereoMode"), UNSIGNED_INTEGER),
                               (16#b0#, To_Unbounded_String("PixelWidth"), UNSIGNED_INTEGER),
                               (16#ba#, To_Unbounded_String("PixelHeight"), UNSIGNED_INTEGER),
                               (16#54aa#, To_Unbounded_String("PixelCropBottom"), UNSIGNED_INTEGER),
                               (16#54bb#, To_Unbounded_String("PixelCropTop"), UNSIGNED_INTEGER),
                               (16#54cc#, To_Unbounded_String("PixelCropLeft"), UNSIGNED_INTEGER),
                               (16#54dd#, To_Unbounded_String("PixelCropRight"), UNSIGNED_INTEGER),
                               (16#54b0#, To_Unbounded_String("DisplayWidth"), UNSIGNED_INTEGER),
                               (16#54ba#, To_Unbounded_String("DisplayHeight"), UNSIGNED_INTEGER),
                               (16#54b2#, To_Unbounded_String("DisplayUnit"), UNSIGNED_INTEGER),
                               (16#54b3#, To_Unbounded_String("AspectRatioType"), UNSIGNED_INTEGER),
                               (16#2eb524#, To_Unbounded_String("ColourSpace"), BINARY),
                               (16#2fb523#, To_Unbounded_String("GammaValue"), FLOAT),

                               (16#e1#, To_Unbounded_String("Audio"), CONTAINER),
                               (16#b5#, To_Unbounded_String("SamplingFrequency"), FLOAT),
                               (16#78b5#, To_Unbounded_String("OutputSamplingFrequency"), FLOAT),
                               (16#9f#, To_Unbounded_String("Channels"), UNSIGNED_INTEGER),
                               (16#7d7b#, To_Unbounded_String("ChannelPositions"), BINARY),
                               (16#6264#, To_Unbounded_String("BitDepth"), UNSIGNED_INTEGER),

                               (16#6d80#, To_Unbounded_String("ContentEncodings"), CONTAINER),
                               (16#6240#, To_Unbounded_String("ContentEncoding"), CONTAINER),
                               (16#5031#, To_Unbounded_String("ContentEncodingOrder"), UNSIGNED_INTEGER),
                               (16#5032#, To_Unbounded_String("ContentEncodingScope"), UNSIGNED_INTEGER),
                               (16#5033#, To_Unbounded_String("ContentEncodingType"), UNSIGNED_INTEGER),
                               (16#5034#, To_Unbounded_String("ContentCompression"), CONTAINER),
                               (16#4254#, To_Unbounded_String("ContentCompAlgo"), UNSIGNED_INTEGER),
                               (16#4255#, To_Unbounded_String("ContentCompSettings"), BINARY),
                               (16#5035#, To_Unbounded_String("ContentEncryption"), CONTAINER),
                               (16#47e1#, To_Unbounded_String("ContentEncAlgo"), UNSIGNED_INTEGER),
                               (16#47e2#, To_Unbounded_String("ContentEncKeyID"), BINARY),
                               (16#47e3#, To_Unbounded_String("ContentSignature"), BINARY),
                               (16#47e4#, To_Unbounded_String("ContentSigKeyID"), BINARY),
                               (16#47e5#, To_Unbounded_String("ContentSigAlgo"), UNSIGNED_INTEGER),
                               (16#47e6#, To_Unbounded_String("ContentSigHashAlgo"), UNSIGNED_INTEGER),

                               (16#1c53bb6b#, To_Unbounded_String("Cues"), CONTAINER),
                               (16#bb#, To_Unbounded_String("CuePoint"), CONTAINER),
                               (16#b3#, To_Unbounded_String("CueTime"), UNSIGNED_INTEGER),
                               (16#b7#, To_Unbounded_String("CueTrackPositions"), CONTAINER),
                               (16#f7#, To_Unbounded_String("CueTrack"), UNSIGNED_INTEGER),
                               (16#f1#, To_Unbounded_String("CueClusterPosition"), UNSIGNED_INTEGER),
                               (16#5378#, To_Unbounded_String("CueBlockNumber"), UNSIGNED_INTEGER),
                               (16#ea#, To_Unbounded_String("CueCodecState"), UNSIGNED_INTEGER),
                               (16#db#, To_Unbounded_String("CueReference"), CONTAINER),
                               (16#96#, To_Unbounded_String("CueRefTime"), UNSIGNED_INTEGER),
                               (16#97#, To_Unbounded_String("CueRefCluster"), UNSIGNED_INTEGER),
                               (16#535f#, To_Unbounded_String("CueRefNumber"), UNSIGNED_INTEGER),
                               (16#eb#, To_Unbounded_String("CueRefCodecState"), UNSIGNED_INTEGER),

                               (16#1941a469#, To_Unbounded_String("Attachments"), CONTAINER),
                               (16#61a7#, To_Unbounded_String("AttachedFile"), CONTAINER),
                               (16#467e#, To_Unbounded_String("FileDescription"), UTF_8_STRING),
                               (16#466e#, To_Unbounded_String("FileName"), UTF_8_STRING),
                               (16#4660#, To_Unbounded_String("FileMimeType"), ASCII_STRING),
                               (16#465c#, To_Unbounded_String("FileData"), BINARY),
                               (16#46ae#, To_Unbounded_String("FileUID"), UNSIGNED_INTEGER),
                               (16#4675#, To_Unbounded_String("FileReferral"), BINARY),

                               (16#1043a770#, To_Unbounded_String("Chapters"), CONTAINER),
                               (16#45b9#, To_Unbounded_String("EditionEntry"), CONTAINER),
                               (16#45bc#, To_Unbounded_String("EditionUID"), UNSIGNED_INTEGER),
                               (16#45bd#, To_Unbounded_String("EditionFlagHidden"), UNSIGNED_INTEGER),
                               (16#45db#, To_Unbounded_String("EditionFlagDefault"), UNSIGNED_INTEGER),
                               (16#45dd#, To_Unbounded_String("EditionFlagOrdered"), UNSIGNED_INTEGER),
                               (16#b6#, To_Unbounded_String("ChapterAtom"), CONTAINER),
                               (16#73c4#, To_Unbounded_String("ChapterUID"), UNSIGNED_INTEGER),
                               (16#91#, To_Unbounded_String("ChapterTimeStart"), UNSIGNED_INTEGER),
                               (16#92#, To_Unbounded_String("ChapterTimeEnd"), UNSIGNED_INTEGER),
                               (16#98#, To_Unbounded_String("ChapterFlagHidden"), UNSIGNED_INTEGER),
                               (16#4598#, To_Unbounded_String("ChapterFlagEnabled"), UNSIGNED_INTEGER),
                               (16#6e67#, To_Unbounded_String("ChapterSegmentUID"), BINARY),
                               (16#6ebc#, To_Unbounded_String("ChapterSegmentEditionUID"), BINARY),
                               (16#63c3#, To_Unbounded_String("ChapterPhysicalEquiv"), UNSIGNED_INTEGER),
                               (16#8f#, To_Unbounded_String("ChapterTrack"), CONTAINER),
                               (16#89#, To_Unbounded_String("ChapterTrackNumber"), UNSIGNED_INTEGER),
                               (16#80#, To_Unbounded_String("ChapterDisplay"), CONTAINER),
                               (16#85#, To_Unbounded_String("ChapString"), UTF_8_STRING),
                               (16#437c#, To_Unbounded_String("ChapLanguage"), ASCII_STRING),
                               (16#437e#, To_Unbounded_String("ChapCountry"), ASCII_STRING),
                               (16#6944#, To_Unbounded_String("ChapProcess"), CONTAINER),
                               (16#6955#, To_Unbounded_String("ChapProcessCodecID"), UNSIGNED_INTEGER),
                               (16#450d#, To_Unbounded_String("ChapProcessPrivate"), BINARY),
                               (16#6911#, To_Unbounded_String("ChapProcessCommand"), CONTAINER),
                               (16#6922#, To_Unbounded_String("ChapProcessTime"), UNSIGNED_INTEGER),
                               (16#6933#, To_Unbounded_String("ChapProcessData"), BINARY),

                               (16#1254c367#, To_Unbounded_String("Tags"), CONTAINER),
                               (16#7373#, To_Unbounded_String("Tag"), CONTAINER),
                               (16#63c0#, To_Unbounded_String("Targets"), CONTAINER),
                               (16#68ca#, To_Unbounded_String("TargetTypeValue"), UNSIGNED_INTEGER),
                               (16#63ca#, To_Unbounded_String("TargetType"), ASCII_STRING),
                               (16#63c5#, To_Unbounded_String("TrackUID"), UNSIGNED_INTEGER),
                               (16#63c9#, To_Unbounded_String("EditionUID"), UNSIGNED_INTEGER),
                               (16#63c4#, To_Unbounded_String("ChapterUID"), UNSIGNED_INTEGER),
                               (16#63c6#, To_Unbounded_String("AttachmentUID"), UNSIGNED_INTEGER),
                               (16#67c8#, To_Unbounded_String("SimpleTag"), CONTAINER),
                               (16#45a3#, To_Unbounded_String("TagName"), UTF_8_STRING),
                               (16#447a#, To_Unbounded_String("TagLanguage"), ASCII_STRING),
                               (16#4484#, To_Unbounded_String("TagDefault"), UNSIGNED_INTEGER),
                               (16#4487#, To_Unbounded_String("TagString"), UTF_8_STRING),
                               (16#4485#, To_Unbounded_String("TagBinary"), BINARY)
                              );
   begin
      for I in MKV_Elements'Range loop
         EBML_Element_Map.Insert(MKV_Elements(I).ID, new EBML_Element_Type'(MKV_Elements(I)));
      end loop;
   end;

end Element;